import {
  WebSocketGateway, // 导入WebSocketGateway装饰器，用于定义WebSocket网关
  WebSocketServer, // 导入WebSocketServer装饰器，用于注入WebSocket服务器实例
  SubscribeMessage, // 导入SubscribeMessage装饰器，用于定义处理WebSocket消息的方法
  ConnectedSocket, // 导入ConnectedSocket装饰器，用于获取连接的WebSocket客户端
  MessageBody,
} from '@nestjs/websockets'; // 从NestJS的WebSocket模块中导入装饰器
import { Socket, Server } from 'socket.io'; // 导入Socket.IO的Socket类型
// 导入远程控制服务
import { RemoteTerminalService } from './app.service';

// 使用WebSocketGateway装饰器定义一个WebSocket网关,监听3210端口
@WebSocketGateway(3210, {
  // 允许跨域
  cors: {
    origin: '*', // 允许所有来源
  },
  // 定义命名空间
  namespace: 'control', // 默认是 /，如果设置成 /control，那么客户端连接的时候，就需要使用 ws://localhost:3210/control 这种形式
})
export class RemoteControlGateway {
  // 注入WebSocket服务器实例，需要向所有客户端广播消息时使用
  @WebSocketServer() server: Server;

  // 定义一个设备类型的字段，用于区分不同类型的设备
  private deviceType = 'linux'; // 默认设备类型为 Linux

  // 构造函数，注入远程控制服务
  constructor(private remoteControlService: RemoteTerminalService) {}

  // 当客户端连接时触发
  handleConnection(client: Socket) {
    console.log(`客户端接入: ${client.id}`);
    // 初始化 SSH 会话
    this.remoteControlService.initializeSession(client.id);
  }

  // 当客户端断开连接时触发
  handleDisconnect(client: Socket) {
    console.log(`客户端断开: ${client.id}`);
  }

  // 处理启动终端会话的请求,传入主机地址、用户名和密码、设备类型
  @SubscribeMessage('startTerminalSession')
  async handleStartTerminalSession(
    @MessageBody()
      data: { host: string; username: string; password: string; type: string },
    @ConnectedSocket() client: Socket,
  ) {
    const clientId = client.id;
    this.deviceType = data.type;
    try {
      // 启动 SSH 会话
      const message = await this.remoteControlService.startSSHSession(
        data.host,
        data.username,
        data.password,
        clientId,
        data.type, // 传递设备类型到服务层
      );
      // 获取 SSH 会话的 shell 流
      const shellStream = this.remoteControlService.getShellStream(clientId);
      if (shellStream) {
        // 监听 shell 流的 data 事件,当主机SSH会话有输出时触发
        shellStream.on('data', (data: Buffer) => {
          // 确保发送的是字符串格式的数据
          client.emit('terminalData', data.toString('utf8'));
        });
      }
      // 发送启动终端会话的成功消息
      client.emit('terminalSessionStarted', { message, clientId });
    } catch (error) {
      // 发送启动终端会话的失败消息
      client.emit('terminalSessionError', error.message);
      // 如果设备类型是路由器交换机，发送退出命令
      if (this.deviceType === 'device') {
        await this.remoteControlService.sendExitCommands(clientId);
      } else {
        // 执行断开设备连接
        this.remoteControlService.disconnect(clientId);
      }
    }
  }

  // 处理终端输入,传入客户端ID和输入的命令
  @SubscribeMessage('input')
  async handleInput(
    @MessageBody() data: { clientId: string; input: string },
    @ConnectedSocket() client: Socket,
  ) {
    if (client.disconnected) {
      console.log(`客户端 ${client.id} 已断开连接，停止处理输入`);
      return;
    }
    try {
      // 根据客户端 ID 获取 SSH 会话的 shell 流
      const shellStream = this.remoteControlService.getShellStream(
        data.clientId,
      );
      // 如果 shell 流存在且可写,将输入写入 shell 流
      if (shellStream && shellStream.writable) {
        // 检查输入是否为退格键，并发送正确的字符
        shellStream.write(data.input);
      } else {
        // 如果 shell 流不存在或不可写,发送错误消息
        client.emit(
          'terminalSessionError',
          'Shell终端不可用,请检查是否已启动终端会话.',
        );
        // 如果设备类型是路由器交换机，发送退出命令
        if (this.deviceType === 'device') {
          await this.remoteControlService.sendExitCommands(data.clientId);
        } else {
          // 执行断开设备连接
          this.remoteControlService.disconnect(data.clientId);
        }
      }
    } catch (error) {
      console.log('处理终端输入时发生错误:', error);
    }
  }

  // 处理断开终端连接的请求,传入客户端ID
  @SubscribeMessage('disconnectTerminal')
  async handleDisconnect1(
    @MessageBody() clientId: string,
    @ConnectedSocket() client: Socket,
  ) {
    console.log('进入 sendExitCommands 断开设备的方法:', clientId);
    // 如果设备类型是路由器交换机，发送退出命令
    if (this.deviceType == 'device') {
      client.emit('terminalDisconnected', '设备终端已断开');
      const message = await this.remoteControlService.sendExitCommands(
        clientId,
      );
      console.log('执行 sendExitCommands 设备命令之后：', message);
    } else {
      client.emit('terminalDisconnected', '系统终端已断开');
      // 执行断开设备连接
      this.remoteControlService.disconnect(clientId);
    }
  }
}
